﻿using CMS.Controls.TabPageTemplate;
using CMS.Infos;
using CMS.REST;
using CMS.Util;
using NPOI.HSSF.UserModel;
using NPOI.HSSF.Util;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace CMS.Controls.DisplayTabPages.ChartTabPages
{
    public class ExportDutyRecordChartTabPage : ChartExportTabPage
    {
        protected override string MainButtonName => "开始导出";

        protected override string ExportName => "导出签到统计";

        protected override void StartExport()
        {
            if (!Directory.Exists(TB_Path.Text))
            {
                _Publisher.WriteLine("ERROR", "必须选择一个存在的保存路径");
                return;
            }
            if (CB_Term.SelectedIndex == -1)
            {
                _Publisher.WriteLine("ERROR", "必须选择一个学期");
                return;
            }
            Utils.PrivateProfile.WriteString("ExportDutyRecord", "Path", TB_Path.Text);

            string path = Path.Combine(TB_Path.Text, DateTime.Now.ToString("签到统计表yyMMddHHmmss") + ".xlsx");
            if (_ExportDutyRecord(path, DTP_StartDate.Value.Date, DTP_EndDate.Value.Date, (int)CB_Term.SelectedValue))
            {
                _Publisher.WriteLine("INFO", "成功导出签到统计表到" + path);
            }
        }

        private DateTimePicker DTP_StartDate;
        private DateTimePicker DTP_EndDate;
        private ComboBox CB_Term;
        private TextBox TB_Path;

        protected override void _InitFormPanel(FormPanel fp)
        {
            DTP_StartDate = new MyDateTimePicker();
            DTP_EndDate = new MyDateTimePicker();

            DTP_StartDate.ValueChanged += (sender, e) =>
            {
                if (DTP_StartDate.Value > DTP_EndDate.Value)
                {
                    DTP_EndDate.Value = DTP_StartDate.Value;
                }
            };
            DTP_EndDate.ValueChanged += (sender, e) =>
            {
                if (DTP_StartDate.Value > DTP_EndDate.Value)
                {
                    DTP_StartDate.Value = DTP_EndDate.Value;
                }
            };

            fp.AddField("开始导出日期", DTP_StartDate);
            fp.AddField("结束导出日期", DTP_EndDate);

            CB_Term = new MyComboBox()
            {
                DropDownStyle = ComboBoxStyle.DropDownList
            };
            RESTResponse response = RESTClient.IndexManaged("apis/v1/terms");
            if (response.IsSuccess)
            {
                CB_Term.DataSource = response.Data.ToObject<Term[]>();
                //CB_Term.Items.AddRange(response.Data.ToObject<Term[]>());
                CB_Term.ValueMember = "ID";
                CB_Term.DisplayMember = "Name";
            }
            fp.AddField("学期", CB_Term);

            TB_Path = new MyTextBox
            {
                Text = Utils.PrivateProfile.ReadString("ExportDutyRecord", "Path", "")
            };

            FolderBrowserDialog FBD = new FolderBrowserDialog();

            fp.AddPathField("导出路径", TB_Path, FBD);

        }


        /// <summary>
        /// 导出签到统计表
        /// </summary>
        private bool _ExportDutyRecord(string file, DateTime startDate, DateTime endDate, int termID)
        {
            Term term;
            _Publisher.WriteLine("INFO", "正在拉取学期数据");
            RESTResponse response = RESTClient.ReadManaged("apis/v1/terms", termID.ToString());
            if (response.IsSuccess)
            {
                term = response.Data.ToObject<Term[]>()[0];
                if (term.StartDate == null)
                {
                    _Publisher.WriteLine("ERROR", "选定的学期的开学时间未填写,请填写后重试");
                    return false;
                }
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取学期数据,导出失败");
                return false;
            }


            _Publisher.WriteLine("INFO", "正在拉取助理信息");
            Assistant[] assistants;
            response = RESTClient.IndexManaged("apis/v1/assistants");
            if (response.IsSuccess)
            {
                assistants = response.Data.ToObject<Assistant[]>();
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取助理信息,导出失败");
                return false;
            }

            _Publisher.WriteLine("INFO", "正在拉取值班安排表");
            DutyArrange[] arranges;
            response = RESTClient.IndexManaged("apis/v1/duty_arranges", new RESTWhere("term_id", termID));
            if (response.IsSuccess)
            {
                arranges = response.Data.ToObject<DutyArrange[]>();
            }
            else
            {
                _Publisher.WriteLine("WARN", "拉取值班安排表失败,将无法根据值班安排表进行决策");
                arranges = null;
            }

            _Publisher.WriteLine("INFO", "正在拉取换课记录");
            //if (response.IsSuccess)
            //{
            //todo 需要考虑进去换课记录
            //}

            _Publisher.WriteLine("INFO", "正在拉取签到数据");
            DutyRecord[] dutyRecords;
            response = RESTClient.IndexManaged("apis/v1/duty_records", new RESTWhere("term_id", termID));
            if (response.IsSuccess)
            {
                dutyRecords = response.Data.ToObject<DutyRecord[]>();
            }
            else
            {
                _Publisher.WriteLine("ERROR", "无法拉取签到数据,导出失败");
                return false;
            }

            _Publisher.WriteLine("INFO", "正在统计数据");

            string[] HeaderStr = new string[]
            {
                "日期" ,
                "周次",
                "星期",
                "地点",
                "姓名",
                "值班节次",
                "应值班节数",
                "实值班时长(分)",
                "应签到时间",
                "实签到时间",
                "应签退时间",
                "实签退时间",
                "备注"
            };

            int[] Widths = new int[]
            {
                8,5,7,8,8,10,10,10,15,8,15,8,10
            };

            TimeSpan[][] Schedule = new TimeSpan[][]
            {
                new TimeSpan[]{TimeSpan.Parse("8:00"), TimeSpan.Parse("9:35") },
                new TimeSpan[]{TimeSpan.Parse("9:55"), TimeSpan.Parse("11:30")},
                new TimeSpan[]{TimeSpan.Parse("11:40"), TimeSpan.Parse("13:15")},
                new TimeSpan[]{TimeSpan.Parse("2:00"), TimeSpan.Parse("15:35")},
                new TimeSpan[]{TimeSpan.Parse("15:55"), TimeSpan.Parse("17:30")},
                new TimeSpan[]{TimeSpan.Parse("18:40"), TimeSpan.Parse("20:15")},
                new TimeSpan[]{ TimeSpan.Parse("20:25"), TimeSpan.Parse("22:00")},
            };

            TimeSpan EarlierThanStartTime1 = new TimeSpan(0, 40, 0);    //开始签到时间距离开始值班时间间隔
            TimeSpan EarlierThanStartTime2 = TimeSpan.Zero;             //结束签到时间距离开始值班时间间隔
            TimeSpan LaterThanEndTime1 = TimeSpan.Zero;                 //开始签退时间距离结束值班时间间隔
            TimeSpan LaterThanEndTime2 = new TimeSpan(0, 40, 0);        //结束签退时间距离结束值班时间间隔


            //计算实际的签到时间
            //startTime1表示开始签到时间,早于此时间属于提前签到
            //startTime2表示结束签到时间,晚于此事件属于迟签到
            //endTime1表示开始签退时间,早于此时间属于早退
            //endTime2表示结束签退时间,迟于此时间属于迟签退
            void CalcClassTimeRaw(uint classTime, out TimeSpan startTime1, out TimeSpan startTime2, out TimeSpan endTime1, out TimeSpan endTime2)
            {
                int startClassTime = Utils.CalcTimeStart(classTime);
                int timeSpan = Utils.CalcTimeSpan(classTime, 14);
                startTime1 = Schedule[startClassTime / 2][0].Subtract(EarlierThanStartTime1);
                startTime2 = Schedule[startClassTime / 2][0].Subtract(EarlierThanStartTime2);
                endTime1 = Schedule[(startClassTime + timeSpan - 1) / 2][1].Add(LaterThanEndTime1);
                endTime2 = Schedule[(startClassTime + timeSpan - 1) / 2][1].Add(LaterThanEndTime2);
            }

            IEnumerable<DutyRecord> DealData()
            {
                List<string> mark = new List<string>();
                Dictionary<int, Assistant> assistantMap = assistants.ToDictionary(a => a.ID);
                for (DateTime cur = startDate; cur <= endDate; cur = cur.AddDays(1))
                {
                    int week = Utils.DayOfWeekToInt(cur.DayOfWeek);
                    int classWeek = Utils.CalcClassWeek(cur, term.StartDateRaw);
                    DutyArrange[] arrangeOfDay = (from d in arranges where (d.DutyClassWeekRaw & classWeek) != 0 && d.Week == week orderby d.DutyClassTimeRaw select d).ToArray();
                    List<DutyRecord> recordOfDay = (from r in dutyRecords where r.DateRaw == cur select r).ToList();
                    foreach (DutyArrange arrange in arrangeOfDay)
                    {
                        CalcClassTimeRaw(arrange.DutyClassTimeRaw, out TimeSpan startTime1, out TimeSpan startTime2, out TimeSpan endTime1, out TimeSpan endTime2);
                        IEnumerable<DutyRecord> personRecord = recordOfDay.Where(r => r.AssistantID == arrange.AssistantID && r.Building == r.Building && r.StartTimeRaw <= endTime1 && r.EndTimeRaw >= startTime2);
                        if (personRecord.Count() > 0)
                        {
                            foreach (DutyRecord r in personRecord.ToArray())
                            {
                                mark.Clear();
                                if (r.StartTimeRaw < startTime1)
                                {
                                    mark.Add("早签");
                                }
                                else if (r.StartTimeRaw > startTime2)
                                {
                                    mark.Add("迟签");
                                }

                                if (r.EndTime == null)
                                {
                                    mark.Add("未签退");
                                }
                                else if (r.EndTimeRaw < endTime1)
                                {
                                    mark.Add("早退");
                                }
                                else if (r.EndTimeRaw > endTime2)
                                {
                                    mark.Add("迟退");
                                }
                                r.Mark = string.Join(",", mark);
                                r.ShouldDutyClassTime = arrange.DutyClassTimeRaw;
                                r.ShouldStartTime = $"{startTime1}-{startTime2}";
                                r.ShouldEndTime = $"{endTime1}-{endTime2}";
                                if (assistantMap.TryGetValue(r.AssistantID, out Assistant assistant))
                                {
                                    r.AssistantName = assistant.Name;
                                }

                                yield return r;
                                recordOfDay.Remove(r);
                            }
                        }
                        else
                        {
                            assistantMap.TryGetValue(arrange.AssistantID, out Assistant assistant);
                            yield return new DutyRecord()
                            {
                                TermID = arrange.TermID,
                                AssistantID = arrange.AssistantID,
                                AssistantName = assistant?.Name,
                                Building = arrange.Building,
                                DateRaw = cur,
                                StartTime = null,
                                EndTime = null,
                                Manual = false,
                                ShouldDutyClassTime = arrange.DutyClassTimeRaw,
                                Mark = "未签"
                            };
                        }
                    }
                    foreach (DutyRecord r in recordOfDay)
                    {
                        r.Mark = "非值班";
                        assistantMap.TryGetValue(r.AssistantID, out Assistant assistant);
                        r.AssistantName = assistant?.Name;
                        yield return r;
                    }
                }
            }

            DutyRecord[] arr = DealData().ToArray();

            IWorkbook workbook;
            if (string.Compare(Path.GetExtension(file), ".xls") == 0)
            {
                workbook = new HSSFWorkbook();
            }
            else
            {
                workbook = new XSSFWorkbook();
            }

            ISheet tmpSheet = workbook.CreateSheet($"{startDate.ToShortDateString()}~{endDate.ToShortDateString()}签到统计".Replace("/", "-"));
            IRow tmpRow = tmpSheet.CreateRow(0);
            ICell tmpCell;
            ICellStyle defaultStyle = workbook.CreateCellStyle();
            defaultStyle.BorderTop = defaultStyle.BorderBottom = defaultStyle.BorderLeft = defaultStyle.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;


            IFont tmpFont = workbook.CreateFont();
            tmpFont.IsBold = true;
            tmpFont.FontHeightInPoints = 12;

            ICellStyle headerStyle = workbook.CreateCellStyle();
            headerStyle.CloneStyleFrom(defaultStyle);
            headerStyle.SetFont(tmpFont);


            for (int i = 0; i < HeaderStr.Length; i++)
            {
                tmpSheet.SetColumnWidth(i, Utils.GetSheetWidth( Widths[i]));
                tmpCell = tmpRow.CreateCell(i);
                tmpCell.CellStyle = headerStyle;
                tmpCell.SetCellValue(HeaderStr[i]);
            }


            ICellStyle warnStyle = workbook.CreateCellStyle();
            warnStyle.CloneStyleFrom(defaultStyle);
            warnStyle.FillForegroundColor = HSSFColor.Yellow.Index;
            warnStyle.FillPattern = FillPattern.SolidForeground;


            int row = 1;
            foreach (DutyRecord record in arr)
            {
                tmpRow = tmpSheet.CreateRow(row);
                ICellStyle tmpStyle = record.Mark != null && (record.Mark.IndexOf("未签到") != -1 || record.Mark.IndexOf("非值班") != -1) ? warnStyle : defaultStyle;

                //签到日期
                tmpCell = tmpRow.CreateCell(0);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.DateRaw.ToShortDateString());

                //签到周次
                //int classWeek = Utils.CalcClassWeek(record.DateRaw, term.StartDateRaw);
                tmpCell = tmpRow.CreateCell(1);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue((record.DateRaw - term.StartDateRaw).Days / 7 + 1);

                //签到星期
                tmpCell = tmpRow.CreateCell(2);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(Utils.DayOfWeekToStr(record.DateRaw.DayOfWeek));

                //签到地点
                tmpCell = tmpRow.CreateCell(3);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.Building);

                //助理姓名
                tmpCell = tmpRow.CreateCell(4);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.AssistantName);

                //应值班时间
                tmpCell = tmpRow.CreateCell(5);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(Utils.ConvertValue(record.ShouldDutyClassTime));

                //应值班节数
                tmpCell = tmpRow.CreateCell(6);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(Utils.CalcTimeSpan(record.ShouldDutyClassTime, 14));

                //实际值班时间
                tmpCell = tmpRow.CreateCell(7);
                tmpCell.CellStyle = tmpStyle;
                if (record.DutyTimeSpan != null)
                {
                    tmpCell.SetCellValue(record.DutyTimeSpan.Value);
                }

                //应签到时间
                tmpCell = tmpRow.CreateCell(8);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.ShouldStartTime);

                //签到时间
                tmpCell = tmpRow.CreateCell(9);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.StartTime);
                if (record.Mark?.IndexOf("迟签") != -1)
                {
                    tmpCell.CellStyle = warnStyle;
                }

                //应签到时间
                tmpCell = tmpRow.CreateCell(10);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.ShouldEndTime);

                //签退时间
                tmpCell = tmpRow.CreateCell(11);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.EndTime);

                if (record.Mark != null && (record.Mark.IndexOf("早退") != -1 || record.Mark.IndexOf("未签退") != -1))
                {
                    tmpCell.CellStyle = warnStyle;
                }

                //结果分析
                tmpCell = tmpRow.CreateCell(12);
                tmpCell.CellStyle = tmpStyle;
                tmpCell.SetCellValue(record.Mark);

                row++;
            }

            while (true)
            {
                try
                {
                    using (FileStream fs = new FileStream(file, FileMode.Create))
                    {
                        workbook.Write(fs);
                        return true;
                    }
                }
                catch (Exception err)
                {
                    if (MessageBox.Show(this, $"遇到错误:{err.Message},希望重试吗?", "保存错误", MessageBoxButtons.YesNo) == DialogResult.No)
                    {
                        return false;
                    }
                }
            }

        }


    }
}
